package com.gmlive.common.network.domain;

import android.content.Context;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.NonNull;

import com.gmlive.common.network.domain.entity.DomainBean;
import com.gmlive.common.network.domain.request.DomainRepository;
import com.google.gson.Gson;
import com.tencent.mmkv.MMKV;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import io.reactivex.Observable;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

/**
 * @Author sven
 * @Date 2020/5/20
 */
public class DomainManager {

    private final static String TAG = "DomainManager";
    private final static String PREFS_KEY = "domains_json";
    private final static String SLB_KEY = "SLB";

    private DomainConfig mConfig = null;
    private boolean mInited = false;
    private Context mAppContext = null;
    private MMKV mMMKV;
    private Gson mGson;
    // 当前动态SLB ip 列表
    private List<String> mSlbIps = null;

    private HashMap<String, String> mDomains = null;

    private DomainRepository mRepo;
    private Disposable mDisposable = null;
    private Disposable mIntervalDisposable = null;
    private DomainPreparedCallback mPreparedCallback = null;

    private AtomicBoolean mInitialized = new AtomicBoolean(false);

    private static volatile DomainManager instance = null;

    public static DomainManager getInstance() {
        if (instance == null) {
            instance = new DomainManager();
        }

        return instance;
    }

    public synchronized void init(@NonNull Context context, @NonNull DomainConfig config) {
        if (mInited) {
            printLog("DomainManager 已经初始化过了，无需重复初始化");
            return;
        }

        mAppContext = context.getApplicationContext();
        MMKV.initialize(mAppContext);
        mMMKV = MMKV.defaultMMKV();
        mGson = new Gson();
        mConfig = config;
        getCachedDomains();

        mInited = true;
    }

    public boolean domainAvailable() {
        return mDomains != null && !mDomains.isEmpty();
    }

    public void setPrepareCallback(DomainPreparedCallback callback) {
        mPreparedCallback = callback;
    }

    public String getDomainForKey(@NonNull String key) {
        assetInitialized();
        assetPrepared();

        String hostForKey = null;
        if (mDomains != null) {
            boolean hasTestKey = false;
            if (mConfig.mDevelopEnv) {
                String devKey = mConfig.mDevEnvKeyPrefix + key;

                if (mDomains.containsKey(devKey)) {
                    hasTestKey = true;
                    hostForKey = mDomains.get(devKey);
                }
            }

            if (!hasTestKey && mDomains.containsKey(key)) {
                hostForKey = mDomains.get(key);
            }
        }
        printLog("key = " + key + ", value = " + hostForKey + ", devEnv = " + mConfig.mDevelopEnv);
        return hostForKey;
    }

    public void startRefreshDomain() {
        assetInitialized();

        mIntervalDisposable = Observable.interval(0, mConfig.mRefreshInterval, TimeUnit.MILLISECONDS)
                .subscribe(new Consumer<Long>() {
                    @Override
                    public void accept(Long aLong) {
                        printLog("执行获取动态域名任务");
                        refreshDomains();
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) {
                        printLog("定期更新域名任务失败，" + throwable.getMessage());
                    }
                });
    }

    public void stopRefreshDomain() {
        if (mIntervalDisposable != null) {
            mIntervalDisposable.dispose();
        }

        if (mDisposable != null) {
            mDisposable.dispose();
        }

        mInitialized.set(false);
    }

    /**
     * 获取动态域名
     */
    private void refreshDomains() {
        assetInitialized();

        if (mRepo == null) {
            mRepo = DomainRepository.get(mConfig.mOkHttpClient);
        }

        if (mSlbIps == null || mSlbIps.isEmpty()) {
            mSlbIps = new ArrayList<>();

            mSlbIps.addAll(mConfig.mDomains);
        }

        final List<String> slbIps = new ArrayList<>(mSlbIps);
        mDisposable = Observable.just(slbIps)
                .subscribeOn(Schedulers.io())
                .flatMap(new Function<List<String>, ObservableSource<DomainBean>>() {
                    @Override
                    public ObservableSource<DomainBean> apply(List<String> ipList) {
                        for (String ip : ipList) {
                            try {
                                DomainBean domain = mRepo.getDomain(ip, mConfig.mApiPath, mConfig.mParams);
                                if (domain != null) {
                                    return Observable.just(domain);
                                }
                            } catch (Exception e) {
                                mConfig.mLogger.log("更新域名配置失败，ip = " + ip + ", err = " + e.getMessage());
                            }
                        }
                        return Observable.error(new Throwable("轮询结束，未拉到域名配置"));
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Consumer<DomainBean>() {
                    @Override
                    public void accept(DomainBean domainBean) {
                        handleSuccess(domainBean);
                        if (mInitialized.compareAndSet(false, true)) {
                            printLog("IP 组轮询成功，获取到动态域名，通知初始化成功");
                            if (mPreparedCallback != null) {
                                mPreparedCallback.onPrepared(true, false);
                            }
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) {
                        printLog("获取动态域名失败, 出现异常: " + throwable.getMessage());
                        if (mInitialized.compareAndSet(false, true)) {
                            printLog("IP 组轮询失败，没有获取到动态域名，通知初始化失败");
                            if (mPreparedCallback != null) {
                                mPreparedCallback.onPrepared(false, false);
                            }
                        }
                    }
                });
    }

    private void getCachedDomains() {
        String json = mMMKV.getString(PREFS_KEY, "");
        parseDomains(json);
    }

    public void parseDomains(String json) {
        try {
            DomainBean domain = mGson.fromJson(json, DomainBean.class);
            if (domain != null && domain.code == 0 && domain.data != null && !domain.data.isEmpty()) {
                if (mDomains == null) {
                    mDomains = domain.data;
                } else {
                    mDomains.clear();
                    mDomains.putAll(domain.data);
                }

                if (mDomains.containsKey(SLB_KEY)) {
                    updateSlbIp(mDomains.get(SLB_KEY));
                }
            }
        } catch (Exception e) {
            printLog("读取缓存失败：" + e.getMessage());
        }
    }

    private void handleSuccess(DomainBean domainBean) {
    	if (domainBean == null
		    || domainBean.data == null
		    || domainBean.data.isEmpty()) return;
        String cacheValue = mGson.toJson(domainBean);
        // 更新文件缓存
        mMMKV.putString(PREFS_KEY, cacheValue);

        if (mDomains == null) {
            mDomains = new HashMap<>();
        }
        // 更新所有的key
        mDomains.putAll(domainBean.data);

        String slbIpString = domainBean.data.get(SLB_KEY);
        updateSlbIp(slbIpString);
    }

    private void updateSlbIp(String ipArrayString) {
        if (mSlbIps != null) {
            mSlbIps.clear();
        }

        if (!TextUtils.isEmpty(ipArrayString)) {
            if (mSlbIps == null) {
                mSlbIps = new ArrayList<>();
            }

            mSlbIps.addAll(Arrays.asList(ipArrayString.split(",")));
        }

        if (mSlbIps == null || mSlbIps.isEmpty()) {
            printLog("缓存的SLB ip列表为：empty");
        } else {
            printLog("缓存的SLB ip列表为：" + mSlbIps);
        }
    }

    public void reportQuality(String ip, long spend, int code) {
        if (mConfig != null && mConfig.mRequestCallback != null) {
            mConfig.mRequestCallback.onRequestEnd(ip, code, spend, "");
        }
    }

    private void assetInitialized() {
        if (!mInited) {
            throw new IllegalStateException("DomainManager 未执行初始化！！");
        }
    }

    private void assetPrepared() {
        if (!mInitialized.get()) {
            Log.i(TAG, "动态域名环境未准备好");
        }
    }

    private void printLog(String message) {
        if (mConfig != null && mConfig.mLogger != null && mConfig.mLoggable) {
            mConfig.mLogger.log(message);
        }
    }
}
